package com.ipan.poi.excel.exporter;

import static com.ipan.poi.excel.log.XlsExportFileLogger.MSG_EXPORT_ENTITY;
import static com.ipan.poi.excel.log.XlsExportFileLogger.MSG_EXPORT_FINISH;
import static com.ipan.poi.excel.log.XlsExportFileLogger.MSG_EXPORT_RESULT;
import static com.ipan.poi.excel.log.XlsExportFileLogger.MSG_EXPORT_START;

import java.io.OutputStream;
import java.sql.ResultSet;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ipan.poi.PoiConfig;
import com.ipan.poi.excel.WorkbookFactory;
import com.ipan.poi.excel.XlsObjectFactory;
import com.ipan.poi.excel.config.XlsEntity;
import com.ipan.poi.excel.log.PoiAppFileLogger;
import com.ipan.poi.excel.log.XlsExportFileLogger;
import com.ipan.poi.excel.service.XlsService;

/**
 * Excel导出接口实现类；
 * 创建：使用工厂类XlsObjectFactory创建，支持多线程共享；
 * 
 * @author iPan
 * @version 2015-01-10
 */
public class XlsExporter implements XlsExportable {
	public static final String EXPORT_LOGGER_NAME = "XlsExport.log";
	public static final String DEFAULT_EXPORT_NAME_FMT = "[export]{0}.xls";
	public static final String DEFAULT_SHEET_NAME = "sheet1";
	
	private XlsWritable writer = null;
//	private CellStyleCreatable styleCreater = null;
	private String exportDir = null;
	private XlsExportFileLogger fileLogger = null;
	private boolean logEnable = false;
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	public XlsExporter() {
		this(XlsObjectFactory.getDefaultWriter());
	}
	
	public XlsExporter(XlsWritable writer) {
		this.writer = writer;
//		this.styleCreater = styleCreater;
		String dir = PoiConfig.getInstance().getExcelLogDir();
		this.exportDir = dir.replace("\\", "/");;
		fileLogger = new XlsExportFileLogger(this.getLoggerPath());
		this.logEnable = PoiConfig.getInstance().isXlsExporterLog();
	}
	
	public void exportByCfg(XlsEntity defEntity, Object entity, OutputStream out, CellStyleCreatable styleCreater) {
		exportByCfg(XlsObjectFactory.getDefaultXlsService(), defEntity, entity, out, styleCreater, 0, DEFAULT_SHEET_NAME, null);
	}
	public void exportByCfg(XlsEntity defEntity, Object entity, OutputStream out, CellStyleCreatable style, int rowIndex, String sheetName) {
		exportByCfg(XlsObjectFactory.getDefaultXlsService(), defEntity, entity, out, style, rowIndex, sheetName, null);
	}
	public void exportByCfg(XlsService<Object> service, XlsEntity defEntity, Object entity, OutputStream out, CellStyleCreatable style, 
			int rowIndex, String sheetName, Workbook workbook) {
		List<Object> entityList = service.searchXls(defEntity, entity);
		doExportByEntity(defEntity, entityList, out, style, rowIndex, sheetName, workbook);
	}

	public void exportByEntity(XlsEntity defEntity, List<?> entityList, OutputStream out, CellStyleCreatable styleCreater) {
		doExportByEntity(defEntity, entityList, out, styleCreater, 0, DEFAULT_SHEET_NAME, null);
	}
	public void exportByEntity(XlsEntity defEntity, List<?> entityList, OutputStream out, CellStyleCreatable style, 
			int rowIndex, String sheetName, Workbook workbook) {
		doExportByEntity(defEntity, entityList, out, style, rowIndex, sheetName, workbook);
	}
	private void doExportByEntity(XlsEntity defEntity, List<?> entityList, OutputStream out, CellStyleCreatable style, 
			int rowIndex, String sheetName, Workbook workbook) {
		beforeExport(defEntity); // to log
		Workbook wb = workbook;
		Sheet sheet = null;
		try {
			if (wb == null) {
				wb = WorkbookFactory.createXSSFWorkbook();
			}
			sheet = wb.createSheet(sheetName);
			writer.writeByEntity(defEntity, sheet, style, rowIndex, entityList);
			wb.write(out);
			exportSuccess(defEntity); // to log
		} catch (Exception e) {
			exportFail(defEntity); // to log
			throw new RuntimeException("Excel输出错误！", e);
		} finally {
			afterExport(defEntity); // to log
			try {
				if (wb != null && wb instanceof SXSSFWorkbook) {
					((SXSSFWorkbook) wb).dispose();
				}
			} catch (Exception e) {
			}
		}
	}

	public void exportByMap(XlsEntity defEntity, List<Map<String, Object>> list, OutputStream out, CellStyleCreatable styleCreater) {
		doExportByMap(defEntity, list, out, styleCreater, 0, DEFAULT_SHEET_NAME, null);
	}
	public void exportByMap(XlsEntity defEntity, List<Map<String, Object>> list, OutputStream out, CellStyleCreatable style, 
			int rowIndex, String sheetName, Workbook workbook) {
		doExportByMap(defEntity, list, out, style, rowIndex, sheetName, workbook);
	}
	private void doExportByMap(XlsEntity defEntity, List<Map<String, Object>> list, OutputStream out, CellStyleCreatable style, 
			int rowIndex, String sheetName, Workbook workbook) {
		beforeExport(defEntity); // to log
		Workbook wb = workbook;
		Sheet sheet = null;
		try {
			if (wb == null) {
				wb = WorkbookFactory.createXSSFWorkbook();
			}
			sheet = wb.createSheet(sheetName);
			writer.writeByMap(defEntity, sheet, style, rowIndex, list);
			wb.write(out);
			exportSuccess(defEntity); // to log
		} catch (Exception e) {
			exportFail(defEntity); // to log
			throw new RuntimeException("Excel输出错误！", e);
		} finally {
			afterExport(defEntity); // to log
			try {
				if (wb != null && wb instanceof SXSSFWorkbook) {
					((SXSSFWorkbook) wb).dispose();
				}
			} catch (Exception e) {
			}
		}
	}
	
	public void exportByResultSet(XlsEntity defEntity, ResultSet resultSet, OutputStream out, CellStyleCreatable styleCreater) {
		exportByResultSet(defEntity, resultSet, out, styleCreater, 0, DEFAULT_SHEET_NAME, null);
	}
	public void exportByResultSet(XlsEntity defEntity, ResultSet resultSet, OutputStream out,
			CellStyleCreatable style, int rowIndex, String sheetName, Workbook workbook) {
		beforeExport(defEntity); // to log
		Workbook wb = workbook;
		Sheet sheet = null;
		try {
			if (wb == null) {
				wb = WorkbookFactory.createXSSFWorkbook();
			}
			sheet = wb.createSheet(sheetName);
			writer.writeByResultSet(defEntity, sheet, style, rowIndex, resultSet);
			workbook.write(out);
			exportSuccess(defEntity); // to log
		} catch (Exception e) {
			exportFail(defEntity); // to log
			throw new RuntimeException("Excel输出错误！", e);
		} finally {
			afterExport(defEntity); // to log
			try {
				if (wb != null && wb instanceof SXSSFWorkbook) {
					((SXSSFWorkbook) wb).dispose();
				}
			} catch (Exception e) {
			}
		}
	}

	private void beforeExport(XlsEntity defEntity) {
		logger.debug("Excel导出文件[{}]开始 ...", this.getFileName(defEntity));
		fileLogger.appendToCache(MSG_EXPORT_START, fileLogger.getDefaultDateStr());
		fileLogger.appendToCache(MSG_EXPORT_ENTITY, defEntity.getClassName());
	}
	
	private void exportSuccess(XlsEntity defEntity) {
		logger.debug("Excel导出文件[{}]成功.", this.getFileName(defEntity));
		fileLogger.appendToCache(MSG_EXPORT_RESULT, PoiAppFileLogger.MSG_SUCCESS);
	}
	
	private void exportFail(XlsEntity defEntity) {
		logger.debug("Excel导出文件[{}]失败.", this.getFileName(defEntity));
		fileLogger.appendToCache(MSG_EXPORT_RESULT, PoiAppFileLogger.MSG_FAIL);
	}
	
	private void afterExport(XlsEntity defEntity) {
		logger.debug("Excel导出文件[{}]结束.", this.getFileName(defEntity));
		fileLogger.appendToCache(MSG_EXPORT_FINISH, fileLogger.getDefaultDateStr());
		// 日志开关
		if (logEnable) {
			fileLogger.flush();
		}
		// 清除缓存
		fileLogger.cleanCache();
	}

	public String getFileName(XlsEntity defEntity) {
		String name = (StringUtils.isNotEmpty(defEntity.getFileName())) ? defEntity.getFileName() 
				: MessageFormat.format(DEFAULT_EXPORT_NAME_FMT, getDateTime());
		if (!name.toLowerCase().endsWith(".xls")) {
			name += ".xls";
		}
		return name;
	}
	
	public String getExportDir() {
		return this.exportDir;
	}
	
	protected String getLoggerPath() {
		return this.exportDir + "/" + EXPORT_LOGGER_NAME;
	}

	private String getDateTime() {
		return new Date().getTime() + "";
	}

}
